using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AvaloniaMaidVSIX.Helpers
{
    public class XmlHelper
    {
        public static string GetAttribute(string text, string attributeFullName)
        {
            if (string.IsNullOrWhiteSpace(text)) return string.Empty;
            if (string.IsNullOrWhiteSpace(attributeFullName)) return string.Empty;
            var startToken = attributeFullName + "=\"";
            var endToken = "\"";

            var startIndex = text.IndexOf(startToken);
            var endIndex = text.IndexOf(endToken, startIndex + startToken.Length);

            if (startIndex < 0) return string.Empty;
            if (endIndex < 0) return string.Empty;

            return text.Substring(startIndex + startToken.Length, endIndex - startIndex - startToken.Length);
        }

        public static string GetAttributeBeforeSpecialToken(string text, string attributeFullName, string specialToken)
        {
            if (string.IsNullOrWhiteSpace(text)) return string.Empty;
            if (string.IsNullOrWhiteSpace(attributeFullName)) return string.Empty;
            var startToken = attributeFullName + "=\"";
            var endToken = "\"";

            var startIndex = text.IndexOf(startToken);
            var endIndex = text.IndexOf(endToken, startIndex + startToken.Length);

            var specialIndex = text.IndexOf(specialToken);

            if (endIndex > specialIndex) return string.Empty;

            if (startIndex < 0) return string.Empty;
            if (endIndex < 0) return string.Empty;

            return text.Substring(startIndex + startToken.Length, endIndex - startIndex - startToken.Length);
        }

        public static bool IsValidBlock(string xmlBlock)
        {
            int counter = 0;
            int startIndex = 0;

            var tokenList = new List<XmlSyntaxToken>();
            var isComment = false;
            while (true)
            {
                var targetIndex = SeekXmlToken(xmlBlock, startIndex);
                if (targetIndex < 0) break;

                var token = GetToken(xmlBlock, targetIndex);

                if (token == XmlSyntaxToken.CommentStart)
                {
                    isComment = true;
                }

                if (isComment == false)
                {
                    tokenList.Add(token);
                }

                else if (token == XmlSyntaxToken.CommentEnd)
                {
                    isComment = false;
                }
                startIndex = targetIndex + 1;
            }

            var isValid = IsValidSerial(tokenList);

            return isValid;

        }

        private enum XmlSyntaxToken
        {
            // < > </ />
            None,

            OnlyOpen,

            OnlyClose,

            SlashOpen,

            SlashClose,

            CommentStart,
            CommentEnd,
        }

        private enum XmlLogicalToken
        {
            None,
            StartPart,
            EndPart,
            Line,
        }

        private static int SeekXmlToken(string xmlBlock, int startIndex)
        {
            if (startIndex < 0) return -1;
            if (startIndex >= xmlBlock.Length) return -1;

            for (int i = startIndex; i < xmlBlock.Length; i++)
            {
                var ch = xmlBlock[i];

                if (ch == '<' || ch == '>')
                {
                    return i;
                }
            }
            return -1;
        }

        private static XmlSyntaxToken GetToken(string xmlBlock, int simpleIndex)
        {
            if (simpleIndex < 0) return XmlSyntaxToken.None;

            var ch = xmlBlock[simpleIndex];

            if (ch == '<')
            {
                var nextIndex = simpleIndex + 1;
                if (nextIndex >= xmlBlock.Length) return XmlSyntaxToken.OnlyOpen;

                var nextCh = xmlBlock[nextIndex];

                if (nextCh == '/') return XmlSyntaxToken.SlashOpen;

                if (nextCh == '!')
                {
                    var commnetIndex = simpleIndex + 3;
                    if (commnetIndex >= xmlBlock.Length) return XmlSyntaxToken.OnlyOpen;
                    var part = xmlBlock.Substring(simpleIndex, 4);
                    if (part == "<!--") return XmlSyntaxToken.CommentStart;
                    else return XmlSyntaxToken.OnlyOpen;
                }

                else return XmlSyntaxToken.OnlyOpen;
            }
            else if (ch == '>')
            {
                var prevIndex = simpleIndex - 1;
                if (prevIndex < 0) return XmlSyntaxToken.OnlyClose;

                var prevCh = xmlBlock[prevIndex];
                if (prevCh == '/') return XmlSyntaxToken.SlashClose;

                if (prevCh == '-')
                {
                    var commnetIndex = simpleIndex - 2;
                    if (commnetIndex < 0) return XmlSyntaxToken.OnlyClose;
                    var part = xmlBlock.Substring(simpleIndex - 2, 3);
                    if (part == "-->") return XmlSyntaxToken.CommentEnd;
                    else return XmlSyntaxToken.OnlyClose;
                }

                else return XmlSyntaxToken.OnlyClose;
            }

            return XmlSyntaxToken.None;
        }

        private static XmlLogicalToken GetToken(XmlSyntaxToken t1, XmlSyntaxToken t2)
        {
            if (t1 == XmlSyntaxToken.OnlyOpen && t2 == XmlSyntaxToken.OnlyClose) return XmlLogicalToken.StartPart;
            if (t1 == XmlSyntaxToken.OnlyOpen && t2 == XmlSyntaxToken.SlashClose) return XmlLogicalToken.Line;
            if (t1 == XmlSyntaxToken.SlashOpen && t2 == XmlSyntaxToken.OnlyClose) return XmlLogicalToken.EndPart;

            return XmlLogicalToken.None;
        }

        private static bool IsValidSerial(List<XmlSyntaxToken> tokenList)
        {
            if (tokenList.Count % 2 == 1) return false;
            var pairCount = tokenList.Count / 2;
            var counter = 0;
            for (var i = 0; i < pairCount; i++)
            {
                var t1 = tokenList[2 * i];
                var t2 = tokenList[2 * i + 1];

                var token = GetToken(t1, t2);

                if (token == XmlLogicalToken.None)
                {
                    return false;
                }
                else if (token == XmlLogicalToken.StartPart)
                {
                    counter++;
                }
                else if (token == XmlLogicalToken.EndPart)
                {
                    counter--;
                    if (counter < 0) return false;
                }
            }
            return counter == 0;
        }
    }
}
